歡迎來到「Python pytest TDD 實戰:從零開始的測試驅動開發」系列!
想像一下,你是一位新手開發者,剛加入一個重視程式品質的團隊。主管交給你第一個任務:「我們要建立一個 Python 專案,而且要從第一天就導入測試文化。」聽起來很有挑戰性?別擔心,讓我們從最基礎的開始,一步步建立起 TDD 的基礎。
今天是我們 TDD 旅程的第一天,我們要設置 Python + pytest 的開發環境,並寫下第一個測試。就像學開車一樣,我們先在安全的練習場地熟悉基本操作。
今天結束後,你將學會:
第一階段:打好基礎(Day 1-10)
├── Day 01 - 環境設置與第一個測試 ★ 今天在這裡
├── ...
└── (更多精彩內容待續)
首先建立一個全新的 Python 專案:
mkdir python-tdd-demo
cd python-tdd-demo
建立虛擬環境:
# macOS/Linux
python -m venv venv
source venv/bin/activate
# Windows
python -m venv venv
venv\Scripts\activate
安裝 pytest 和相關套件:
pip install pytest pytest-cov
建立 pytest.ini
配置檔:
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts =
-v
--tb=short
--strict-markers
--disable-warnings
建立 requirements-dev.txt
:
pytest>=7.0.0
pytest-cov>=4.0.0
建立基本的專案結構:
python-tdd-demo/
├── src/
│ └── __init__.py
├── tests/
│ └── __init__.py
├── pytest.ini
├── requirements-dev.txt
└── README.md
建立空的 src/__init__.py
和 tests/__init__.py
:
mkdir src tests
touch src/__init__.py tests/__init__.py
讓我們從最簡單的例子開始 - 計算機函數。
建立 tests/day01/test_calculator.py
:
# 先寫測試,這時 calculator 模組還不存在
from src.calculator import add
def test_adds_two_numbers_correctly():
result = add(2, 3)
assert result == 5
pytest
你會看到測試失敗的訊息,因為 calculator
模組還不存在。這就是 TDD 的「紅燈」階段 - 先寫測試,看它失敗。
建立 src/calculator.py
:
def add(a: int, b: int) -> int:
return a + b
再次執行測試:
pytest
現在測試應該通過了!這就是「綠燈」階段。
更新 tests/day01/test_calculator.py
:
import pytest
from src.calculator import add, subtract, multiply, divide
def test_adds_two_numbers_correctly():
result = add(2, 3)
assert result == 5
def test_adds_negative_numbers_correctly():
result = add(-2, 3)
assert result == 1
def test_subtracts_two_numbers_correctly():
result = subtract(5, 3)
assert result == 2
def test_multiplies_two_numbers_correctly():
result = multiply(4, 3)
assert result == 12
def test_divides_two_numbers_correctly():
result = divide(10, 2)
assert result == 5
def test_handles_division_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
更新 src/calculator.py
:
def add(a: int, b: int) -> int:
return a + b
def subtract(a: int, b: int) -> int:
return a - b
def multiply(a: int, b: int) -> int:
return a * b
def divide(a: int, b: int) -> float:
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
pytest tests/
所有測試都應該通過!你也可以執行特定測試檔案:
pytest tests/day01/test_calculator.py
pytest 支援函數風格的測試,這更符合 Python 的簡潔哲學。所有測試都是簡單的函數,使用 assert
進行驗證。
pytest 提供多種輸出選項:
# 詳細輸出
pytest -v
# 顯示覆蓋率
pytest --cov=src
# 產生 HTML 覆蓋率報告
pytest --cov=src --cov-report=html
# 顯示最慢的測試
pytest --durations=10
# 只執行失敗的測試
pytest --lf
# 顯示本地變數值(除錯用)
pytest -l
# 停在第一個失敗
pytest -x
在開始 TDD 的第一天,你可能會遇到一些問題:
如果遇到模組匯入錯誤,確保:
src/__init__.py
檔案存在如果 pytest 找不到測試:
test_
開頭test_
開頭pytest.ini
配置是否正確assert
: 簡單直觀的斷言語法pytest.raises
: 測試異常處理tests/
目錄tests/day01/test_calculator.py
)test_
前綴命名測試檔案和測試函數->
標註回傳型別每個測試都應該遵循 AAA 模式:
試著為計算機新增以下功能:
modulo(a, b)
函數power(base, exponent)
函數💡 提示:記得先寫測試再實作功能!
今天我們完成了 TDD 學習旅程的第一步!透過建立 Python + pytest 環境,我們學會了:
今天我們成功建立了 Python + pytest 的測試環境,並完成了第一個單元測試。雖然例子很簡單,但我們已經體驗了完整的 TDD 流程:
記住,TDD 不只是一種測試技術,更是一種設計思維。透過先寫測試,我們被迫思考程式的介面和行為,這能幫助我們寫出更好的程式碼。
明天我們將深入了解斷言函數的威力,學習如何寫出更有表達力的測試。準備好了嗎?繼續加油! 💪